#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
#include <linux/stat.h>
#include <linux/sysfs.h>
#include <linux/string.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include "my_led_pinctrl_gpio_subsystem.h"
static struct my_led_dev* my_led = NULL;

static int my_led_open(struct inode* inode, struct file* filp)
{
	struct my_led_dev* dev = container_of(inode->i_cdev, 
								struct my_led_dev, cdev); 
	filp->private_data = dev;
	return 0;
}
/******************************设置led对应的gpio值******************************/
static void set_led_gpio(int gpio, int val, int* status,int* cnt)
{
	// 非0-灯亮，0-灯灭，GPIO引脚输出低电平灯亮，输出高点平灯灭
	if (val == 0) {
		gpio_set_value(gpio, LED_OFF);
		*status = LED_OFF;		
	}
	else {
		gpio_set_value(gpio, LED_ON);
		if (*status != LED_ON) {
			*cnt = *cnt + 1;
			*status = LED_ON;
		} 
	}	
}

static ssize_t my_led_write(struct file* filp, const char __user* buf, 
				size_t size, loff_t* ppos)
{
	struct my_led_dev* dev = filp->private_data;
	int ret, val[3];
	if (size != sizeof(val)) return -EINVAL; 
	// 成功返回0,失败返回失败的数目
	ret = copy_from_user(&val, buf, sizeof(val)); 
	if (0 != ret) return -EFAULT;

	set_led_gpio(dev->r_gpio, val[0], &dev->r_status, &dev->r_on_cnt);
	set_led_gpio(dev->g_gpio, val[1], &dev->g_status, &dev->g_on_cnt);
	set_led_gpio(dev->b_gpio, val[2], &dev->b_status, &dev->b_on_cnt);

	return sizeof(val);
}
// 文件操作函数结构体
static const struct file_operations my_led_fops = {
	.owner = THIS_MODULE,
	.write = my_led_write,
	.open = my_led_open,
};

/******************************初始化和注册cdev结构体******************************/
static int set_up_my_led_cdev(struct my_led_dev* dev, int cnt)
{
	int err;
	cdev_init(&dev->cdev, &my_led_fops);
	dev->cdev.owner = THIS_MODULE;
	err = cdev_add(&dev->cdev, dev->devno, cnt); // 出错返回负值
	if (err < 0)
		printk(KERN_ERR "adding my_led cdev %d error, errno %d\n", cnt, err);
	return err;
}
/*******************************定义设备的属性********************************/
static ssize_t red_show(struct device* dev, struct device_attribute* attr,char* buf)
{
	return sprintf(buf, "red led lighting times %u\n", my_led->r_on_cnt);
} // echo 非0，灯亮，echo 0,灯灭
static ssize_t red_store(struct device *dev, struct device_attribute *attr,  
                				const char *buf, size_t count)
{	int err;
	unsigned int val;
	// 将echo输入得字符串转换为无符号整数，10表示十进制
	err = kstrtouint(buf, 10, &val); 
	if (err)
		return err;
	set_led_gpio(my_led->r_gpio, val, &my_led->r_status, &my_led->r_on_cnt);
	return count;
} 
static ssize_t green_show(struct device *dev, struct device_attribute *attr,char *buf)
{
	return sprintf(buf, "green led lighting times %u\n", my_led->g_on_cnt);
}
static ssize_t green_store(struct device *dev, struct device_attribute *attr,  
                				const char *buf, size_t count)
{	int err;
	unsigned int val;
	err = kstrtouint(buf, 10, &val);
	if (err)
		return err;
	set_led_gpio(my_led->g_gpio, val, &my_led->g_status, &my_led->g_on_cnt);
	return count;
} 
static ssize_t blue_show(struct device *dev, struct device_attribute *attr,char *buf)
{
	return sprintf(buf, "blue led lighting times %u\n", my_led->b_on_cnt);
}
static ssize_t blue_store(struct device *dev, struct device_attribute *attr,  
                				const char *buf, size_t count)
{	int err;
	unsigned int val; 
	err = kstrtouint(buf, 10, &val);
	if (err)
		return err;
	set_led_gpio(my_led->b_gpio, val, &my_led->b_status, &my_led->b_on_cnt);
	return count;
} 
// 红色led设备属性，生成的属性结构体名称为dev_attr_red，类型为struct device_attribute，
// mode为0644，所属者可以读写，其他只能读
static DEVICE_ATTR(red, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, red_show, red_store);
// 绿色led设备属性，生成的属性结构体名称为dev_attr_green，类型为struct device_attribute，
// mode为0644，所属者可以读写，其他只能读
static DEVICE_ATTR(green, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, green_show, green_store);
// 蓝色led设备属性，生成的属性结构体名称为dev_attr_blue，类型为struct device_attribute，
// mode为0644，所属者可以读写，其他只能读
static DEVICE_ATTR(blue, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, blue_show, blue_store);
static struct attribute* led_colour_attrs[] = {
	&dev_attr_red.attr,
	&dev_attr_green.attr,
	&dev_attr_blue.attr,
	NULL,
};
/* 创建led颜色属性组，生成的属性结构体名称为led_colour_group，类型为static const 
   struct attribute_group，使用led_colour_attrs初始化
   ATTRIBUTE_GROUPS(led_colour); */
// 使用ATTRIBUTE_GROUPS宏创建属性组时编译会出现警告，这里直接定义led颜色属性组
static struct attribute_group led_colour_group = {
    .attrs = led_colour_attrs,
};

/****************从设备树中提取信息，初始化gpio*****************/
static int led_gpio_init(struct my_led_dev* dev)
{
	int ret = 0, ret1 = 0, ret2 = 0, ret3 = 0;
	struct device_node* n;
	const char* status;
	// 设置led引脚的名字
	memcpy(dev->r_name, READ_PIN_NAME, strlen(READ_PIN_NAME) + 1);
	memcpy(dev->g_name, GREEN_PIN_NAME, strlen(GREEN_PIN_NAME) + 1);
	memcpy(dev->b_name, BLUE_PIN_NAME, strlen(BLUE_PIN_NAME) + 1);

	// 从设备树中获取设备节点
	n = of_find_node_by_path("/rgb_led");
	if (NULL == n) {
		printk(KERN_ERR "find led node error by /rgb_led\n");
		return -EINVAL;
	}
	dev->node = n;
	// 根据设备树中的gpio属性，获取led对应的gpio编号
	dev->r_gpio = of_get_named_gpio(dev->node, "red", 0);
	if (dev->r_gpio < 0) {
		printk(KERN_ERR "can't get %s gpio number\n", dev->r_name);
		return -EINVAL;
	}
	printk(KERN_INFO "%s gpio number %#x\n", dev->r_name, (unsigned int)dev->r_gpio);
	dev->g_gpio = of_get_named_gpio(dev->node, "green", 0);
	if (dev->g_gpio < 0) {
		printk(KERN_ERR "can't get %s gpio number\n", dev->g_name);
		return -EINVAL;
	}
	printk(KERN_INFO "%s gpio number %#x\n", dev->g_name, (unsigned int)dev->g_gpio);
	dev->b_gpio = of_get_named_gpio(dev->node, "blue", 0);
	if (dev->b_gpio < 0) {
		printk(KERN_ERR "can't get %s gpio number\n", dev->b_name);
		return -EINVAL;
	}
	printk(KERN_INFO "%s gpio number %#x\n", dev->b_name, (unsigned int)dev->b_gpio);

	// 申请gpio
	ret = gpio_request(dev->r_gpio, dev->r_name);
	if (ret < 0) {
		printk(KERN_ERR "request %s gpio error\n", dev->r_name);
		return -EINVAL;		
	}
	ret = gpio_request(dev->g_gpio, dev->g_name);
	if (ret < 0) {
		printk(KERN_ERR "request %s gpio error\n", dev->g_name);
		ret = -EINVAL;
		goto free_read_gpio;		
	}
	ret = gpio_request(dev->b_gpio, dev->b_name);
	if (ret < 0) {
		printk(KERN_ERR "request %s gpio error\n", dev->b_name);
		ret = -EINVAL;
		goto free_green_gpio;	
	}

	// 从设备树中获取status属性
	ret = of_property_read_string(dev->node, "status", &status);
	if (ret < 0) {
		printk(KERN_ERR "find rgb_led node status property error\n");
		ret = -EINVAL;
		goto free_blue_gpio;
	}
	
	// 设置GPIO为输入，并根据status属性设置输出值
	ret = strcmp(status, "okay");
	if (ret == 0) {  
		ret1 = gpio_direction_output(dev->r_gpio, LED_ON);
		dev->r_status = LED_ON;
		ret2 = gpio_direction_output(dev->g_gpio, LED_ON);
		dev->g_status = LED_ON;
		ret3 = gpio_direction_output(dev->b_gpio, LED_ON);
		dev->b_status = LED_ON;
	} else {
		ret1 = gpio_direction_output(dev->r_gpio, LED_OFF);
		dev->r_status = LED_OFF;
		ret2 = gpio_direction_output(dev->g_gpio, LED_OFF);
		dev->g_status = LED_OFF;
		ret3 = gpio_direction_output(dev->b_gpio, LED_OFF);
		dev->b_status = LED_OFF;
	}
	if (ret1 < 0 || ret2 < 0 || ret3 < 0) {
		printk(KERN_ERR "gpio set direction output error\n");
		ret = -EINVAL;
		goto free_blue_gpio;
	}
	return 0;
free_blue_gpio:
	gpio_free(dev->b_gpio);
free_green_gpio:
	gpio_free(dev->g_gpio);
free_read_gpio:
	gpio_free(dev->r_gpio);
	return ret;
}
/****************************模块初始化************************************/
static int __init my_led_init(void)
{
	int ret = 0;
	dev_t devno = 0;
	// 动态分配设备号，传入的devno参数为0,使用unregister_chrdev_region注销动态分配的设备号
	ret = alloc_chrdev_region(&devno, 0, 1, "my_led");
	if (ret < 0) {
		printk(KERN_ERR "alloc_chrdev_region() failed %d\n", ret);
		return ret;	
	}
	// 分配设备结构体内存并将分配的内存清0
	my_led = kzalloc(sizeof(struct my_led_dev), GFP_KERNEL);
	if (NULL == my_led) {
		ret = -ENOMEM;
		printk(KERN_ERR "kzalloc() failed %d\n", ret);
		goto unreg_chrdev;
	}
	my_led->devno = devno;

	// 从设备树获取资源
	ret = led_gpio_init(my_led);
	if (0 != ret) goto free_dev;

	ret = set_up_my_led_cdev(my_led, 1);
	if (ret < 0) goto free_dev;
	// 创建类和设备，当模块加载后会自动在/dev目录下生成设备节点
	my_led->my_led_class = class_create(THIS_MODULE, "my_led_class");
	if (IS_ERR(my_led->my_led_class)) {
		ret = PTR_ERR(my_led->my_led_class);
		printk(KERN_ERR "class_create() failed %d\n", ret);
		goto del_cdev;
	}
	my_led->my_led_device = device_create(my_led->my_led_class, NULL, 
								devno, NULL, "my_led");
	if (IS_ERR(my_led->my_led_device)) {
		ret = PTR_ERR(my_led->my_led_device);
		printk(KERN_ERR "device_create() failed %d\n", ret);
		goto clean_class;
	}
	// 使用sysfs_create_group可以创建一组属性文件，sysfs_remove_group移除一组属性文件
	// 使用sysfs_create_groups可以创建多组属性文件，ysfs_remove_groups移除多组属性文件
	ret = sysfs_create_group(&my_led->my_led_device->kobj, &led_colour_group);
	if(ret != 0) goto clean_device;

	// 初始化互斥体
	mutex_init(&my_led->mutex);
	printk(KERN_INFO "my_led module init OK, major %u, minor %u\n", 
			MAJOR(devno), MINOR(devno));
	return 0;
clean_device:
	device_destroy(my_led->my_led_class, devno);
clean_class: 
	class_destroy(my_led->my_led_class);
del_cdev:
	cdev_del(&my_led->cdev);
free_dev:
	kfree(my_led);
	my_led = NULL;
unreg_chrdev:
	unregister_chrdev_region(devno, 1);
    return ret;
}

/********************模块注销************************/
static void __exit my_led_exit(void)
{
	sysfs_remove_group(&my_led->my_led_device->kobj, &led_colour_group);
	device_destroy(my_led->my_led_class, my_led->devno);
	class_destroy(my_led->my_led_class);
	cdev_del(&my_led->cdev);
	gpio_free(my_led->b_gpio);  // 释放gpio
	gpio_free(my_led->g_gpio);
	gpio_free(my_led->r_gpio);
	unregister_chrdev_region(my_led->devno, 1);
	kfree(my_led);
	my_led = NULL;
	printk(KERN_INFO "my_led module exit\n");
}

module_init(my_led_init);
module_exit(my_led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("liyang.plus@foxmail.com");
MODULE_VERSION("v1.00");